//! Database types relating to group storage.

use std::collections::BTreeMap;

use aspect_codec::prelude::*;
use aspect_ident::DeviceIdent;

/// ID for a message within the context of a group.
pub type GroupMsgId = u64;

/// Stored message entry that we would eventually display to the user.
#[derive(Clone, Debug)]
pub enum MsgEntry {
    Client(ClientMsg),
    App(AppMsg),
}

aspect_codec::inst_enum_codec! {
    MsgEntry u8 {
        0 => Client(field ClientMsg),
        1 => App(field AppMsg),
    }
}

/// Messages inserted into the group log from the Aspect client.
#[derive(Clone, Debug)]
pub struct ClientMsg {
    payload: Vec<u8>,
}

aspect_codec::inst_struct_codec! {
    ClientMsg {
        payload: Vec<u8>,
    }
}

/// Stored application-layer message.
#[derive(Clone, Debug)]
pub struct AppMsg {
    sender: DeviceIdent,
    epoch: u64,
    application_payload: Vec<u8>,
}

impl AppMsg {
    pub fn new(sender: DeviceIdent, epoch: u64, application_payload: Vec<u8>) -> Self {
        Self {
            sender,
            epoch,
            application_payload,
        }
    }
}

aspect_codec::inst_struct_codec! {
    AppMsg {
        sender: DeviceIdent,
        epoch: u64,
        application_payload: Vec<u8>,
    }
}

/// Batch of updates to write to the keystore for some group.
#[derive(Clone, Debug)]
pub struct KeyUpdateBatch {
    writes: BTreeMap<Vec<u8>, KeyUpdateOp>,
}

impl KeyUpdateBatch {
    pub fn new_empty() -> Self {
        Self {
            writes: BTreeMap::new(),
        }
    }

    pub fn len(&self) -> usize {
        self.writes.len()
    }

    pub fn is_empty(&self) -> bool {
        self.writes.is_empty()
    }

    pub fn into_iter(self) -> impl Iterator<Item = (Vec<u8>, KeyUpdateOp)> {
        self.writes.into_iter()
    }

    /// Returns the value stored for a key.
    pub fn get(&self, k: &[u8]) -> Option<&KeyUpdateOp> {
        self.writes.get(k)
    }

    /// Sets the value of a key.
    pub fn set_op(&mut self, k: &[u8], v: KeyUpdateOp) {
        self.writes.insert(k.to_vec(), v);
    }
}

/// Operation to make on a key.
#[derive(Clone, Debug)]
pub enum KeyUpdateOp {
    Set(Vec<u8>),
    Del,
}

impl KeyUpdateOp {
    pub fn as_val(&self) -> Option<&[u8]> {
        match self {
            Self::Set(v) => Some(&v),
            Self::Del => None,
        }
    }
}
